package com.martin.ads.omoshiroilib.util;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.opengl.GLES20;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;

import com.github.yoojia.qrcode.qrcode.QRCodeEncoder;
import com.imageim.yimirror.base.BaseApplication;
import com.imageim.yimirror.common.network.ThreadPoolManager;
import com.imageim.yimirror.common.util.Base64;
import com.imageim.yimirror.common.util.DensityUtil;
import com.imageim.yimirror.common.util.LogUtil;
import com.martin.ads.omoshiroilib.camera.IWorkerCallback;
import com.martin.ads.omoshiroilib.debug.removeit.PixelBuffer;
import com.martin.ads.omoshiroilib.filter.helper.FilterType;
import com.martin.ads.omoshiroilib.glessential.GLImageRender;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.IntBuffer;

//import android.util.Base64;

/**
 * Created by Ads on 2016/11/8.
 */
public class BitmapUtils {
    private static final String TAG = "BitmapUtils";
    //    private static final int pWidth = 1440;
    //    private static final int pHeight = 1080;

    public static Bitmap getScreenShot(int width, int height) {
        IntBuffer pixelBuffer = IntBuffer.allocate(width * height);
        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
                pixelBuffer);
        int[] pixelMirroredArray = new int[width * height];
        int[] pixelArray = pixelBuffer.array();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                pixelMirroredArray[(height - i - 1) * width + j] = pixelArray[i * width + j];
            }
        }
        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        return bmp;
    }

    private static IntBuffer pixelBuffer;

    public static void sendImage(int width, int height, Context context, FileUtils.FileSavedCallback fileSavedCallback) {
        //                final IntBuffer pixelBuffer = IntBuffer.allocate(redPacketWidth * redPacketHeight);
        //        pixelBuffer.reset();
        if (pixelBuffer == null) {
            pixelBuffer = IntBuffer.allocate(width * height);
        }
        //        pixelBuffer.clear();
        //about 20-50ms
        //        long start = System.nanoTime();
        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
                pixelBuffer);
        //        long end = System.nanoTime();
        //        Log.d(TAG, "glReadPixels time: " + (end - start) / 1000000 + " ms");

        //about 700-4000ms(png) 200-1000ms(jpeg)
        //use jpeg instead of png to save time
        //it will consume large memory and may take a long time, depends on the phone
        new SaveBitmapTask(pixelBuffer, width, height, context, fileSavedCallback)
                .executeOnExecutor(ThreadPoolManager.getInstance().getThreadPool());
    }

    private static class SaveBitmapTask extends AsyncTask<Void, Integer, Boolean> {
        long start;
        Context context;
        IntBuffer rgbaBuf;
        int width, height;

        String filePath;
        FileUtils.FileSavedCallback fileSavedCallback;

        public SaveBitmapTask(IntBuffer rgbaBuf, int width, int height, Context context, FileUtils.FileSavedCallback fileSavedCallback) {
            this.rgbaBuf = rgbaBuf;
            this.width = width;
            this.height = height;
            this.context = context;
            File picFolder = BaseApplication.getContext().getCacheDir();
            //            File picFolder= Environment.getExternalStorageDirectory().getAbsoluteFile();
            if (!picFolder.exists()) {
                picFolder.mkdirs();
            }
            filePath = picFolder.getAbsolutePath() + FileUtils.getPicName();
            this.fileSavedCallback = fileSavedCallback;
        }

        @Override
        protected void onPreExecute() {
            start = System.nanoTime();
            super.onPreExecute();
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            saveIntBufferAsBitmap(rgbaBuf, filePath, width, height);
            return true;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            Log.d(TAG, "saveBitmap time: " + (System.nanoTime() - start) / 1000000 + " ms");
            super.onPostExecute(aBoolean);
            //Toast.makeText(context,"ScreenShot is saved to "+filePath, Toast.LENGTH_LONG).toast();
            fileSavedCallback.onFileSaved(filePath);
        }
    }

    private static int[] pixelMirroredArray;
    private static Bitmap bmp;

    private static void saveIntBufferAsBitmap(IntBuffer buf, String filePath, int width, int height) {
        mkDirs(filePath);
        //                int[] pixelMirroredArray = new int[redPacketWidth * redPacketHeight];
        if (pixelMirroredArray == null) {
            pixelMirroredArray = new int[width * height];
        }
        BufferedOutputStream bos = null;
        try {
            int[] pixelArray = buf.array();
            // rotate 180 deg with x axis because y is reversed
            for (int i = 0; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    pixelMirroredArray[(height - i - 1) * width + j] = pixelArray[i * width + j];
                }
            }
            bos = new BufferedOutputStream(new FileOutputStream(filePath));
            if (bmp == null) {
                bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
            }
            //            Bitmap bmp = Bitmap.createBitmap(redPacketWidth, redPacketHeight, Bitmap.Config.ARGB_4444);
            bmp.copyPixelsFromBuffer(IntBuffer.wrap(pixelMirroredArray));
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            //            bmp.recycle();
            //            bmp = null;
            pixelArray = null;
            //            pixelMirroredArray = null;
            buf.clear();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.flush();
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //            System.gc();
        }
    }

    private static void saveBitmap(final Bitmap bitmap, final String outputFilePath, final IWorkerCallback workerCallback) {
        mkDirs(outputFilePath);
        try {
            BufferedOutputStream bos = null;
            try {
                bos = new BufferedOutputStream(new FileOutputStream(outputFilePath));
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            if (workerCallback != null) {
                workerCallback.onPostExecute(null);
            }
        } catch (final Exception e) {
            if (workerCallback != null) {
                workerCallback.onPostExecute(e);
            }
        }
    }

    public static void saveByteArray(final byte[] input, final String outputFilePath, final IWorkerCallback workerCallback, final Handler handler) {
        mkDirs(outputFilePath);
        FakeThreadUtils.postTask(new Runnable() {
            @Override
            public void run() {
                try {
                    FileOutputStream outputStream = new FileOutputStream(outputFilePath);
                    outputStream.write(input);
                    outputStream.flush();
                    outputStream.close();

                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (workerCallback != null) {
                                workerCallback.onPostExecute(null);
                            }
                        }
                    });
                } catch (final Exception e) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (workerCallback != null) {
                                workerCallback.onPostExecute(e);
                            }
                        }
                    });
                }
            }
        });
    }


    public static void saveBitmapWithFilterApplied(final Context context, final FilterType filterType, final Bitmap bitmap, final String outputFilePath, final IWorkerCallback workerCallback) {
        FakeThreadUtils.postTask(new Runnable() {
            @Override
            public void run() {
                Logger.updateCurrentTime();
                GLImageRender imageRender = new GLImageRender(context, bitmap, filterType);
                PixelBuffer buffer = new PixelBuffer(bitmap.getWidth(), bitmap.getHeight());
                Logger.logPassedTime("new PixelBuffer");
                buffer.setRenderer(imageRender);
                Bitmap result = buffer.getBitmap();
                bitmap.recycle();
                Logger.logPassedTime("getBitmap");
                buffer.destroy();
                saveBitmap(result, outputFilePath, workerCallback);
                result.recycle();
                //FIXME: this sucks
                System.gc();
            }
        });

    }

    public static Bitmap loadBitmapFromFile(String filePath) {
        //                BitmapFactory.Options options=new BitmapFactory.Options();
        //                options.inScaled=false;
        Bitmap bitmap = BitmapFactory.decodeFile(filePath);
        return bitmap;
    }

    public static Bitmap loadBitmapWithClip(String filePath, Rect rect) {
        Bitmap bitmap = loadBitmapFromFile(filePath);
        if (rect.left < 0 || rect.top < 0) {
            return bitmap;
        }
        bitmap = Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.width()
                , rect.height(), null, false);
        return bitmap;
    }

    public static Bitmap clipBitmap(Bitmap bitmap, Rect rect) {
        if (rect.left < 0 || rect.top < 0) {
            return bitmap;
        }
        bitmap = Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.width()
                , rect.height(), null, false);
        return bitmap;
    }

    public static Bitmap loadBitmapFromAssets(Context context, String filePath) {
        InputStream inputStream = null;
        try {
            inputStream = context.getResources().getAssets().open(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (inputStream == null) {
            return null;
        }
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
        return bitmap;
    }

    public static Bitmap loadBitmapFromRaw(Context context, int resourceId) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
        return bitmap;
    }

    public static void mkDirs(String fileName) {
        File file = new File(fileName);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
    }

    public static void savePNGBitmap(final Bitmap bitmap, final String outputFilePath) {
        mkDirs(outputFilePath);
        try {
            BufferedOutputStream bos = null;
            try {
                bos = new BufferedOutputStream(new FileOutputStream(outputFilePath));
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }

    public static void saveJPGBitmap(final Bitmap bitmap, final String outputFilePath) {
        mkDirs(outputFilePath);
        try {
            BufferedOutputStream bos = null;
            try {
                bos = new BufferedOutputStream(new FileOutputStream(outputFilePath));
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }

    public static Bitmap loadARGBBitmapFromRGBAByteArray(byte[] bytes, int width, int height, boolean ignoreAlphaChannel) {
        int totSize = width * height;
        if (bytes.length != totSize * 4) {
            throw new RuntimeException("Illegal argument");
        }
        int histogram[] = new int[totSize];
        for (int i = 0, pos = 0; i < totSize; i++, pos += 4) {
            histogram[i] = 0;
            histogram[i] |= (((int) bytes[pos + 0]) & 0xff) << 16;
            histogram[i] |= (((int) bytes[pos + 1]) & 0xff) << 8;
            histogram[i] |= (((int) bytes[pos + 2]) & 0xff) << 0;
            histogram[i] |= ignoreAlphaChannel ? 0xff000000 : (((int) bytes[pos + 3]) & 0xff) << 24;
        }
        return Bitmap.createBitmap(histogram, width, height, Bitmap.Config.ARGB_8888);
    }


    /**
     * bitmap转为base64
     *
     * @param bitmap
     * @return
     */
    public static String bitmapToBase64(Bitmap bitmap, int quality) {

        String result = null;
        ByteArrayOutputStream baos = null;
        try {
            if (bitmap != null) {
                baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
                baos.flush();
                baos.close();
                byte[] bitmapBytes = baos.toByteArray();
                //                result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
                result = Base64.encode(bitmapBytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (baos != null) {
                    baos.flush();
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * base64转为bitmap
     *
     * @param base64Data
     * @return
     */
    public static Bitmap base64ToBitmap(String base64Data) {
        //        byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);
        byte[] bytes = Base64.decode(base64Data);
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    }

    public static Bitmap getQrcodeBitmap(String content, int widthDp) {
        Bitmap qrCodeImage = new QRCodeEncoder.Builder()
                // 二维码图案的宽度
                .width(DensityUtil.dip2px(BaseApplication.getContext(), widthDp))
                .height(DensityUtil.dip2px(BaseApplication.getContext(), widthDp))
                // 二维码的内边距
                .paddingPx(0)
                // 二维码的外边距
                .marginPt(0)
                //                .centerImage(centerImage) // 二维码中间图标
                .build()
                .encode(content);
        return qrCodeImage;
    }

    public static Bitmap getQrcodeBitmap(String content, int widthDp, int color) {
        Bitmap qrCodeImage = new QRCodeEncoder.Builder()
                // 二维码图案的宽度
                .width(DensityUtil.dip2px(BaseApplication.getContext(), widthDp))
                .height(DensityUtil.dip2px(BaseApplication.getContext(), widthDp))
                // 二维码的内边距
                .paddingPx(0)
                // 二维码的外边距
                .marginPt(0)
                .pointColor(color)
                //                .centerImage(centerImage) // 二维码中间图标
                .build()
                .encode(content);
        return qrCodeImage;
    }

    /**
     * 添加padding
     */
    public static Bitmap getQrcodeBitmapWithPadding(String content, int widthDp, int color, int padding) {
        Bitmap qrCodeImage = new QRCodeEncoder.Builder()
                .width(DensityUtil.dip2px(BaseApplication.getContext(), widthDp))
                .height(DensityUtil.dip2px(BaseApplication.getContext(), widthDp))
                .paddingPx(DensityUtil.dip2px(BaseApplication.getContext(), padding))
                .marginPt(0)
                .pointColor(color)
                //                .centerImage(centerImage) // 二维码中间图标
                .build()
                .encode(content);
        return qrCodeImage;
    }

    /**
     * 获得背景透明图todo
     *
     * @param bitmap
     * @param confidenceArray
     * @return
     */
    public static Bitmap getTransBitmap(Bitmap bitmap, float[] confidenceArray) {
        Bitmap transBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        for (int y = 0; y < bitmap.getHeight(); y++) {
            for (int x = 0; x < bitmap.getWidth(); x++) {
                if (y < bitmap.getHeight() / 3) {
                    transBitmap.setPixel(x, y, Color.TRANSPARENT);
                } else {
                    transBitmap.setPixel(x, y, bitmap.getPixel(x, y));
                }
            }
        }
        return transBitmap;
    }

    /**
     * 获得背景和前景合成的图片
     *
     * @param bg
     * @param forground
     * @param width
     * @param height
     * @return
     */
    public static Bitmap getMixBitmap(Bitmap bg, Bitmap forground, int width, int height) {
        if (bg != null) {
            bg = Bitmap.createScaledBitmap(bg, width, height, true);
        }
        if (forground != null) {
            forground = Bitmap.createScaledBitmap(forground, width, height, true);
        }
        Bitmap dstBodyBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(dstBodyBitmap);
        Paint paint = new Paint();
        if (bg != null) {
            canvas.drawBitmap(bg, 0, 0, paint);
        }
        if (forground != null) {
            canvas.drawBitmap(forground, 0, 0, paint);
        }
        return dstBodyBitmap;
    }

    /**
     * 将黑白二值图改为
     *
     * @param twoValueBitmap
     * @return
     */
    public static Bitmap getAroundTransBitmap(Bitmap twoValueBitmap) {
        Bitmap bitmap = Bitmap.createBitmap(twoValueBitmap.getWidth(), twoValueBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        for (int row = 0; row < bitmap.getHeight(); row++) {
            for (int col = 0; col < bitmap.getWidth(); col++) {
                int color = 0;
                try {
                    color = twoValueBitmap.getPixel(col, row);
                } catch (Exception e) {
                    LogUtil.e("data", "row : " + row + ", col : " + col);
                }
                if (color == Color.BLACK) {
                    bitmap.setPixel(col, row, Color.TRANSPARENT);
                } else {
                    bitmap.setPixel(col, row, Color.WHITE);
                }
            }
        }
        return bitmap;
    }
}
